package com.glodon.paas.account.security.oauth1.provider;

import com.glodon.paas.account.dao.OAuthConsumerDao;
import com.glodon.paas.account.dao.domain.OAuthTokenEntity;
import net.oauth.*;
import net.oauth.server.OAuthServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author Don Li
 */
@Component("oauthProvider")
public class SimpleOAuthProvider {
    public final OAuthValidator VALIDATOR = new SimpleOAuthValidator();
    @Autowired
    private OAuthTokenManager tokenMgr;
    @Autowired
    private OAuthConsumerDao consumerStore;

    private String realm = "default";

    public String getRealm() {
        return realm;
    }

    public void setRealm(String realm) {
        this.realm = realm;
    }

    public OAuthConsumer getConsumer(OAuthMessage requestMessage) throws IOException, OAuthException {
        final String consumer_key = requestMessage.getConsumerKey();
        OAuthConsumer consumer = consumerStore.readConsumer(consumer_key); // load from cache
        if (consumer == null) {
            throw new OAuthException("invalid consumer key");
        }
        return consumer;
    }

    private OAuthAccessor createAccessor(OAuthTokenEntity token) throws OAuthException {
        final String consumer_key = token.getConsumerKey();
        OAuthConsumer consumer = consumerStore.readConsumer(consumer_key);
        if (consumer == null) {
            throw new OAuthException("invalid consumer key");
        }

        OAuthAccessor accessor = new OAuthAccessor(consumer);
        if (token.isAccessToken()) {
            accessor.accessToken = token.getValue();
        } else {
            accessor.requestToken = token.getValue();
        }

        accessor.tokenSecret = token.getSecret();
//        OAuthAuthorizationEntity auth = tokenMgr.getAuthorization(token.getoAuthAuthorizationId());
//        if (auth != null) {
//            accessor.setProperty("user", auth.getUserId());
//            accessor.setProperty("authorized", Boolean.TRUE);
//        }
        accessor.setProperty("authorized", token.isAuthorized());
        accessor.setProperty("user", token.getUserId());
        return accessor;
    }

    public OAuthAccessor getAccessor(OAuthMessage requestMessage) throws IOException, OAuthException {
        return getAccessor(requestMessage.getToken());
    }

    public OAuthAccessor getAccessor(String tokenValue) throws OAuthException {
        OAuthTokenEntity token = tokenMgr.getToken(tokenValue);
        return createAccessor(token);
    }

    public void markAsAuthorized(OAuthAccessor accessor, String userId) throws OAuthException {
        accessor.setProperty("user", userId);
        accessor.setProperty("authorized", Boolean.TRUE);

        tokenMgr.authorizeRequestToken(accessor.requestToken, null, userId);
    }

    public void markAsDenied(OAuthAccessor accessor, String userId) throws OAuthException {
        String tokenValue = accessor.requestToken;
        accessor.requestToken = null;
        accessor.accessToken = null;
        accessor.tokenSecret = null;
        accessor.setProperty("authorized", Boolean.FALSE);
        tokenMgr.removeToken(tokenValue);
    }


    /**
     * Generate a fresh request token and secret for a consumer.
     *
     * @throws OAuthException
     */
    public void generateRequestToken(OAuthAccessor accessor) throws OAuthException {
        String consumer_key = accessor.consumer.consumerKey;
        OAuthTokenEntity token = tokenMgr.createUnauthorizedRequestToken(consumer_key, accessor.consumer.callbackURL);

        accessor.requestToken = token.getValue();
        accessor.tokenSecret = token.getSecret();
        accessor.accessToken = null;
    }

    /**
     * Generate a fresh access token and secret for a consumer.
     *
     * @throws OAuthException
     */
    public void generateAccessToken(OAuthAccessor accessor) throws OAuthException {

        OAuthTokenEntity token = tokenMgr.createAccessToken(accessor.requestToken);

        accessor.requestToken = null;
        accessor.tokenSecret = token.getSecret();
        accessor.accessToken = token.getValue();

    }

    public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response, boolean sendBody) throws IOException, ServletException {
        String realm = (request.isSecure()) ? "https://" : "http://";
        realm += request.getServerName();
        if (e instanceof OAuthException && e.getLocalizedMessage().startsWith("Invalid token:")) {
            // todo send user to a more meaningful page
            response.getWriter().write("token timeout, please authorze again");
        } else {
            OAuthServlet.handleException(response, e, realm, sendBody);
        }
    }

    public OAuthTokenManager getTokenMgr() {
        return tokenMgr;
    }

    public OAuthConsumerDao getConsumerStore() {
        return consumerStore;
    }
}
